#define DEBUG 1     // 显示调试
#define SHOWNO 0    // 字段显示前，先变黑
#define SHOWSLOW 1  // 显示慢一点，保证字段翻转

#if DEBUG == 1
#define debug(x) Serial.print(x)
#define debugln(x) Serial.println(x)
#else
#define debug(x)
#define debugln(x)
#endif

// ########################################################################################################

#include <NTPClient.h>  // 1.NTPClient (修改过的)

// ========================================================================================================
// wifi
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.aliyun.com", 8 * 60 * 60, 5 * 60 * 1000);  // 偏移8小时，更新间隔5分钟
// ========================================================================================================

// wifimanager can run in a blocking mode or a non blocking mode
// Be sure to know how to process loops with no delay() if using non blocking
bool wm_nonblocking = true;  // change to true to use non blocking

WiFiManager wm;  // global wm instance
// WiFiManagerParameter custom_field;  // global param ( for non blocking w params )

// --------------------------------------------------------------------------------------------------------
// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
#define _TIMERINTERRUPT_LOGLEVEL_ 0
//
// Select a Timer Clock
#define USING_TIM_DIV1 false   // for shortest and most accurate timer
#define USING_TIM_DIV16 false  // for medium time and medium accurate timer
#define USING_TIM_DIV256 true  // for longest timer but least accurate. Default
//
#include "ESP8266TimerInterrupt.h"  // 4.ESP8266TimerInterrupt
//
#define TIMER_INTERVAL_MS 50  // 50ms
//
// Init ESP8266 timer 1
ESP8266Timer ITimer;
// --------------------------------------------------------------------------------------------------------

#include "vari.h"   // 变量
#include "func0.h"  // 函数
#include "func1.h"  // 函数
#include "func2.h"  // 函数
#include "func3.h"  // 函数
#include "func.h"   // 函数
#include "time.h"   // time函数
#include "mac.h"    // 不同的esp8266

bool wifi_cancel = false;  // wifi检测忽略
bool wifi_ok = false;      // wifi正常
bool key_press = false;    // 按键了

bool setup_mo = false;  // 设置状态
bool nigth_le = false;  // 晚上休息了
bool wuxiu_le = false;  // 中午休息了

volatile bool readrtc = false;
volatile bool readntp = false;

volatile char comchar;        // 串口字符命令
volatile bool click = false;  // 是否按键

// 定时中断子程序，50ms
void IRAM_ATTR TimerHandler() {
  static int j, l;
  static int n;

  // 1s
  n++;
  if (n >= 20) {
    n = 0;
    seco++;
    if (seco >= 60) {
      seco = 0;
      minu++;
      if (minu >= 60) {
        minu = 0;
        hour++;
        if (hour >= 24) {
          hour = 0;
          minu = 0;
          seco = 0;
        }
      }
    }
  }

  // 2s
  l++;
  if (l >= 40) {
    l = 0;
    readrtc = true;
  }

  // 1 hour
  j++;
  if (j >= 1200 * 60) {
    j = 0;
    readntp = true;
  }
}

void wifiInfo() {
  debugln("[WIFI] MAC: " + (String)WiFi.macAddress());
  debugln("[WIFI] SSID: " + (String)wm.getWiFiSSID());
  debugln("[WIFI] PASS: " + (String)wm.getWiFiPass());
}

void ntpInfo() {
  // 显示当前时间
  debugln("[NTP] ----------------------------");
  debug("[NTP] Now Time: ");
  debug(hour);
  debug("/");
  debug(minu);
  debug("/");
  debug(seco);
  debugln(" - DOW: 0");
  //
  // 显示互联网时间
  debug("[NTP] NTP Time: ");
  debugln(ntptime);
  debugln("[NTP] ----------------------------");
}

void setup() {
  Serial.begin(115200);

  pinMode(KEY_D0, INPUT_PULLUP);

#if DEBUG == 1
  Serial.setDebugOutput(true);
#else
  Serial.setDebugOutput(false);
#endif

  debugln("\n MySEGClock by xtbanban(88185785@qq.com)");
  debugln("\n Starting...");

  ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler);

  // ====================================================================
  if (wm_nonblocking) wm.setConfigPortalBlocking(false);
  //
  // // test custom html(radio)
  // const char* custom_radio_str = "<br/><label for='cfid'>Please Setting...</label><br><input type='radio' name='cfid' value='1' checked> Set 00:00<br><input type='radio' name='cfid' value='2'> Set 10:56<br><input type='radio' name='cfid' value='3'> Set NTP<br><br><input type='radio' name='cfid' value='4'> Set Neith<br><input type='radio' name='cfid' value='5'> Set Day";
  // new (&custom_field) WiFiManagerParameter(custom_radio_str);
  // //
  // wm.addParameter(&custom_field);
  // wm.setSaveParamsCallback(saveParamCallback);
  // wm.setMinimumSignalQuality(10); // 默认8%
  wm.setSaveConfigCallback(saveConfigCallback);
  // wm.setConfigPortalTimeout(180);  // 【不适用】等待3分钟过去后，无论结果如何，autoConnect函数都会返回
  wm.setBreakAfterConfig(true);  // 按保存键后，不管是否链接wifi成功，退出配网页面，autoConnect函数返回
  //
  std::vector<const char*> menu = { "wifi", "sep" };  // { "wifi", "param", "sep" };
  wm.setMenu(menu);
  //
  // wm.setClass("invert"); // 暗黑界面
  //
  // 显示是否有预先保存好的wifi信息
  debugln("[WIFI] --------WIFI SAVED----------");
  wifiInfo();
  debugln("[WIFI] ----------------------------");
  //
  GetMySEG(WiFi.macAddress());
  segmet0::getsvar();
  segmet1::getsvar();
  segmet2::getsvar();
  segmet3::getsvar();
  //
  segmet0::show8();
  segmet1::show8();
  segmet2::show8();
  segmet3::show8();
  delay(1000);
  //
  if (!wm.autoConnect("MySEGClock-WiFi")) {
    // 非阻塞模式，立即执行以下，不等按保存键
    debugln("[WIFI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    debugln("[WIFI] ##      [autoConnect]     ##");
    debugln("[WIFI] !!!!  WIFI NOT CONNECT  !!!! ");
    wifiInfo();
    debugln("[WIFI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    wifi_ok = false;
  } else {
    // WiFi自动连接成功（采用上次参数）
    debugln("[WIFI] ----------------------------");
    debugln("[WIFI] ##      [autoConnect]     ##");
    debugln("[WIFI] == WIFI CONNECT SUCCESS . ==");
    wifiInfo();
    debugln("[WIFI] ----------------------------");
    wifi_ok = true;
    SetRTCTime();
    ntpInfo();
  }
}

void saveConfigCallback() {
  // 设置了 wm.setBreakAfterConfig(true) ，只要按保存键，都执行本程序
  if (WiFi.status() != WL_CONNECTED) {
    debugln("[WIFI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    debugln("[WIFI] ##  [saveConfigCallback]  ##");
    debugln("[WIFI] == WIFI NOT CONNECT !!!   ==");
    wifiInfo();
    debugln("[WIFI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    wifi_ok = false;
  } else {
    debugln("[WIFI] ----------------------------");
    debugln("[WIFI] ##  [saveConfigCallback]  ##");
    debugln("[WIFI] == WIFI CONNECT SUCCESS . ==");
    wifiInfo();
    debugln("[WIFI] ----------------------------");
    wifi_ok = true;
    SetRTCTime();
    ntpInfo();
  }
  // loop中立即显示信息
  readrtc = true;
  num = 0;
}

// String getParam(String name) {
//   //read parameter from server, for customhmtl input
//   String value;
//   if (wm.server->hasArg(name)) {
//     value = wm.server->arg(name);
//   }
//   return value;
// }

// void saveParamCallback() {
//   String gp = getParam("cfid");
//   Serial.println("[CALLBACK] saveParamCallback fired");
//   Serial.println("PARAM customfieldid = " + gp);
//   if (gp == String('1')) {
//     hour = 0;
//     minu = 0;
//     seco = 0;
//   } else if (gp == String('2')) {
//     hour = 10;
//     minu = 56;
//     seco = 0;
//   } else if (gp == String('3')) {
//     wifi_ok = SetRTCTime();
//     num = 0;
//   } else if (gp == String('4')) {
//     nigth_le = true;
//   } else if (gp == String('5')) {
//     nigth_le = false;
//     num = 0;
//   }
// }

void loop() {
  // avoid delays() in loop when non-blocking and other long running code
  if (wm_nonblocking) wm.process();

  // 按键检测
  if ((digitalRead(KEY_D0) == LOW) && key_press) {
    debugln("[KEY] Key D0 press...");  // 配网页面是否还在运行
    pinMode(KEY_D0, INPUT_PULLUP);
    key_press = false;
    wuxiu_le = false;
    num = 4;
    wifi_ok = WiFi.status() == WL_CONNECTED;
    comchar = 'c';
    click = true;
  }

  if (readrtc) {
    key_press = true;
  }

  // 设置模式
  if (setup_mo) {
    readrtc = 0;
    readntp = 0;
  }

  // 每2秒钟
  if (readrtc && !setup_mo) {
    if (wm.getConfigPortalActive()) {
      debugln("[WIFI] ConfigPortalActive...");  // 配网页面是否还在运行
    } else {
      debug("[Time] ");
      debug(hour);
      debug(":");
      debug(minu);
      debug(":");
      debugln(seco);
    }

    minu_0 = minu / 10 % 10 + 48;
    minu_1 = minu % 10 + 48;
    hour_0 = hour / 10 % 10 + 48;
    hour_1 = hour % 10 + 48;

#if SHOWNO == 1
    if (num == 10) {
      num = 0;
    }
#endif

    // 过了一分钟
    if (minu_1 != minu_b) {
      hhmm = hour * 100 + minu;
      minu_b = minu_1;
#if SHOWNO == 1
      num = 10;
      comchar = 'h';
      click = true;
#else
      num = 0;

      if (hhmm >= 1210 && !wuxiu_le && !click) {
        wuxiu_le = true;
      }
      if (hhmm >= 1450 && wuxiu_le) {
        wuxiu_le = false;
      }
#endif
    }

    wifi_ok = WiFi.status() == WL_CONNECTED;
  }

  if (!wifi_ok && !wifi_cancel) {
    // wifi错误，可以按键忽略wifi，但是应有RTC模块
    if (readrtc && !setup_mo) {
      if (wm.getConfigPortalActive()) {
        // 配网页面还在运行
        debugln("[WIFI] Wifi wait connect...");
        // 立即显示并每分钟执行显示一次
        if (num == 0) {
          num++;
          debugln("[SHOW] Show: i|i|i|i|");
          SetSegtx();
        }
      } else {
        // 配网页面不运行了，每分钟执行一次
        if (num == 0) {
          num++;
          debugln("[SHOW] Show: i|i|i|i|");  // 先显示天线提示
          SetSegtx();
          //
          // 已经保存了wifi信息，试着连接10s
          const char* ssid = wm.getWiFiSSID().c_str();      // 需要连接到的WiFi名
          const char* password = wm.getWiFiPass().c_str();  // 连接的WiFi密码
          WiFi.mode(WIFI_STA);                              // 设置Wifi工作模式为STA,默认为AP+STA模式
          WiFi.begin(ssid, password);                       // 通过wifi名和密码连接到Wifi
          debug("\r\nConnecting to ");                      // 串口监视器输出网络连接信息
          debug((String)wm.getWiFiSSID());                  // 显示无线ap名称
          debugln(" ...");                                  // 显示正在尝试WiFi连接
          //
          int i = 0;                             // 检查WiFi是否连接成功
          while (WiFi.status() != WL_CONNECTED)  // WiFi.status()函数的返回值是由的WiFi连接状态所决定的。
          {                                      // 如果WiFi连接成功则返回值为WL_CONNECTED
            delay(1000);                         // 此处通过While循环让每隔一秒钟检查一次WiFi.status()函数返回值
            debug("waiting for ");
            debug(i++);
            debugln("s...");
            if (i > 10) {
              debugln("Connecting break, no connect!!!");
              break;
            }
          }
          //
          if (WiFi.status() == WL_CONNECTED) {
            debugln("[WIFI] == WIFI CONNECT SUCCESS . ==");
            wifi_ok = true;
            SetRTCTime();
            ntpInfo();
          } else {
            SetSeghx();
            debugln("[SHOW] Show: - - - -");
            // 连接不成功，提示重启系统
            debugln("[RESET] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            debugln("[RESET] Please RESET System !!! ----");
            debugln("[RESET] And Manage Wifi again !!! --");
            debugln("[RESET] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
          }
        }
      }
    }

  } else {
    // wifi正确； wifi错误，但已经忽略，但是应有RTC模块
    if (readntp && wifi_ok && !setup_mo) {
      // 每小时联网取得标准时间
      readntp = false;
      //
      debugln("[NTP] Get NTP every Hour ...........");
      //
      if (SetRTCTime()) {
        // 成功联网取得ntp时间，刷新显示
        ntpInfo();
        num = 0;
      } else {
        // 联网取得时间错误
        debugln("[NTP] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        debugln("[NTP] NTP Error !!! --------------");
        debugln("[NTP] !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        SetSeghx();
        num = 4;
      }
    }

    // 从个位开始逐个显示
    if (nigth_le || wuxiu_le) {
      if (num == 0) {
        comchar = 'h';
        click = true;
        num = 4;
      }
    } else if (num == 0) {
      num++;
      set = 3;
      comchar = minu_1;
      click = true;
      debug("[SHOW] show num3: ");
    } else if (num == 1) {
      num++;
      set = 2;
      comchar = minu_0;
      click = true;
      debug("[SHOW] show num2: ");
    } else if (num == 2) {
      num++;
      set = 1;
      comchar = hour_1;
      click = true;
      debug("[SHOW] show num1: ");
    } else if (num == 3) {
      num++;
      set = 0;
      comchar = hour_0;
      click = true;
      debug("[SHOW] show num0: ");
    }
  }

  if (readrtc) {
    readrtc = false;
  }

  // 循环从串口读到数据，或者key置位
  while (Serial.available() > 0 || click) {
    if (click) {
      click = false;
    } else {
      comchar = Serial.read();
    }
    debugln(comchar);

    switch (comchar) {
      // 根据set变量，选择4位之一显示0到9
      case '0':
        SetSegnum0();
        break;
      case '1':
        SetSegnum1();
        break;
      case '2':
        SetSegnum2();
        break;
      case '3':
        SetSegnum3();
        break;
      case '4':
        SetSegnum4();
        break;
      case '5':
        SetSegnum5();
        break;
      case '6':
        SetSegnum6();
        break;
      case '7':
        SetSegnum7();
        break;
      case '8':
        SetSegnum8();
        break;
      case '9':
        SetSegnum9();
        break;
      //
      case 'h':  // 全不显示，全黑
        segmet0::showno();
        segmet1::showno();
        segmet2::showno();
        segmet3::showno();
        break;
      case 'c':  // 取消wifi模式
        if (wifi_ok) {
          SetRTCTime();
          ntpInfo();
          num = 0;
        } else {
          wifi_cancel = true;
          debugln("[CLICK] Wifi cancel...");
        }
        break;
      case 'n':  // 白天了，退出黑暗
        nigth_le = true;
        debugln("[CLICK] Nigth comming...");
        num = 0;
        break;
      case 'd':  // 晚上了，黑暗
        nigth_le = false;
        debugln("[CLICK] Sun day now...");
        num = 0;
        break;
      case 'r':  // 清除设置，重启
        debugln("***************************");
        debugln("***************************");
        debugln("System reset and restart!!!");
        debugln("***************************");
        debugln("***************************");
        wm.resetSettings();
        delay(1000);
        ESP.restart();
        break;
      case 's':  // 进入设置模式
        setup_mo = true;
        if (set == 0 && seg == 0) {
          debugln("[SETUP] setup mode, show: 8 8 8 8 ...");
          segmet0::show8();
          segmet1::show8();
          segmet2::show8();
          segmet3::show8();
          delay(500);
        }
        SetSegmo(seg, set);
        seg_b = seg;
        set_b = set;
        debug("[SETUP] set =");
        debug(set);
        debug(" seg =");
        debugln(seg);
        seg++;
        if (seg >= 7) {
          seg = 0;
          set++;
        }
        if (set == 4 && seg == 1) {
          debugln("[SETUP] setup segment over .");
          set = 0;
          seg = 0;
        }
        break;
      case 'q':  // 退出设置模式，重新刷新显示
        debugln("[SETUP] setup mode quit !!!");
        set = 0;
        seg = 0;
        setup_mo = false;
        readrtc = true;
        num = 0;
        break;
      case 'f':  // 翻转这个字段
        if (setup_mo) {
          switch (set_b) {
            case 0:
              SetSegmo_0(seg_b);
              break;
            case 1:
              SetSegmo_1(seg_b);
              break;
            case 2:
              SetSegmo_2(seg_b);
              break;
            case 3:
              SetSegmo_3(seg_b);
              break;
          }
        } else {
          debugln("[SETUP] No setup mode !!!");
        }
        break;
    }
  }
}